/*
* (C) 2007-2012 Alibaba Group Holding Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Authors:
* wuhua <wq163@163.com> , boyan <killme2008@gmail.com>
*/
package com.taobao.metamorphosis.example;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAResource;
import com.taobao.metamorphosis.client.XAMessageSessionFactory;
import com.taobao.metamorphosis.client.producer.PartitionSelector;
import com.taobao.metamorphosis.client.producer.RoundRobinPartitionSelector;
import com.taobao.metamorphosis.client.producer.XAMessageProducer;
import com.taobao.metamorphosis.exception.MetaClientException;
/**
* XA����ģ�壬���ڼ�ʹ��XA���������ݿ������meta������Ϣ��
*
* @author boyan(boyan@taobao.com)
* @date 2011-8-29
*
*/
public class XATransactionTemplate {
private XADataSource xaDataSource;
private XAMessageSessionFactory xaMessageSessionFactory;
private XAMessageProducer xaMessageProducer;
private PartitionSelector partitionSelector;
private TransactionManager transactionManager;
private int transactionTimeout;
private Set<String> publishTopics = new HashSet<String>();
private boolean wasInit = false;
public XATransactionTemplate() {
super();
}
public XATransactionTemplate(final TransactionManager transactionManager, final XADataSource xaDataSource,
final XAMessageSessionFactory xaMessageSessionFactory) {
super();
this.xaDataSource = xaDataSource;
this.xaMessageSessionFactory = xaMessageSessionFactory;
this.transactionManager = transactionManager;
}
public TransactionManager getTransactionManager() {
return this.transactionManager;
}
public void publishTopic(final String topic) {
try {
this.init();
final XAMessageProducer producer = this.getXAMessageProducer();
producer.publish(topic);
this.publishTopics.add(topic);
}
catch (final Exception e) {
throw new XAWrapException(e);
}
}
public Set<String> getPublishTopics() {
return this.publishTopics;
}
public void setPublishTopics(final Set<String> publishTopics) {
this.publishTopics = publishTopics;
}
public void setTransactionManager(final TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
private synchronized void init() throws SystemException {
if (this.wasInit) {
return;
}
this.wasInit = true;
if (this.getTransactionManager() == null) {
throw new IllegalArgumentException("null tm");
}
if (this.getXaDataSource() == null) {
throw new IllegalArgumentException("null XADatasource");
}
if (this.getXaMessageSessionFactory() == null) {
throw new IllegalArgumentException("null XAMessageSessionFactory");
}
final XAMessageProducer producer = this.getXAMessageProducer();
for (final String topic : this.publishTopics) {
producer.publish(topic);
}
this.transactionManager.setTransactionTimeout(this.transactionTimeout);
}
static interface WrapExecutor {
public Object run() throws Exception;
}
private XAConnection getXAConnection() throws SQLException {
final XADataSource xads = this.getXaDataSource();
if (xads == null) {
throw new IllegalArgumentException("Null xaDataSource");
}
return xads.getXAConnection();
}
public Object executeCallback(final XACallback callback) {
XAMessageProducer producer = null;
XAConnection conn = null;
Transaction tx = null;
Connection sqlConn = null;
try {
this.init();
producer = this.getXAMessageProducer();
conn = this.getXAConnection();
tx = this.beginTx(producer, conn);
sqlConn = conn.getConnection();
final Object rt = callback.execute(sqlConn, producer);
this.commitOrRollbackTx(producer, conn, tx, false);
return rt;
}
catch (final Exception e) {
try {
this.commitOrRollbackTx(producer, conn, tx, true);
}
catch (final Exception ex) {
throw new XAWrapException(ex);
}
throw new XAWrapException("Execute xa transaction callback error", e);
}
finally {
if (sqlConn != null) {
try {
sqlConn.close();
}
catch (final Exception e) {
throw new XAWrapException("Close jdbc connection failed", e);
}
}
}
}
private Transaction beginTx(final XAMessageProducer producer, final XAConnection conn) throws SystemException,
MetaClientException, SQLException, RollbackException, NotSupportedException {
this.transactionManager.begin();
final Transaction tx = this.transactionManager.getTransaction();
if (tx == null) {
throw new IllegalStateException("Could not get transaction from tm");
}
final XAResource metaXares = producer.getXAResource();
final XAResource jdbcXares = conn.getXAResource();
tx.enlistResource(metaXares);
tx.enlistResource(jdbcXares);
return tx;
}
private void commitOrRollbackTx(final XAMessageProducer producer, final XAConnection conn, final Transaction tx,
final boolean error) throws Exception {
if (tx == null) {
return;
}
int flag = XAResource.TMSUCCESS;
final XAResource metaXares = producer.getXAResource();
final XAResource jdbcXares = conn.getXAResource();
if (error) {
flag = XAResource.TMFAIL;
}
tx.delistResource(metaXares, flag);
tx.delistResource(jdbcXares, flag);
if (error) {
this.getTransactionManager().rollback();
}
else {
this.getTransactionManager().commit();
}
}
public int getTransactionTimeout() {
return this.transactionTimeout;
}
public void setTransactionTimeout(final int transactionTimeout) {
this.transactionTimeout = transactionTimeout;
}
private synchronized XAMessageProducer getXAMessageProducer() {
if (this.xaMessageProducer != null) {
return this.xaMessageProducer;
}
final XAMessageSessionFactory xasf = this.getXaMessageSessionFactory();
PartitionSelector ps = this.getPartitionSelector();
if (ps == null) {
ps = new RoundRobinPartitionSelector();
}
this.xaMessageProducer = xasf.createXAProducer(ps);
return this.xaMessageProducer;
}
public PartitionSelector getPartitionSelector() {
return this.partitionSelector;
}
public void setPartitionSelector(final PartitionSelector partitionSelector) {
this.partitionSelector = partitionSelector;
}
public XADataSource getXaDataSource() {
return this.xaDataSource;
}
public void setXaDataSource(final XADataSource xaDataSource) {
this.xaDataSource = xaDataSource;
}
public XAMessageSessionFactory getXaMessageSessionFactory() {
return this.xaMessageSessionFactory;
}
public void setXaMessageSessionFactory(final XAMessageSessionFactory xaMessageSessionFactory) {
this.xaMessageSessionFactory = xaMessageSessionFactory;
}
}